package com.bsd.xxyp.rdlock.context;

import com.bsd.xxyp.rdlock.entity.LockInfo;
import com.bsd.xxyp.rdlock.parse.LockAnnotationFieldParse;
import com.bsd.xxyp.rdlock.parse.LockEvaluationContextProvider;
import com.bsd.xxyp.rdlock.parse.LockExpressionEvaluator;
import com.bsd.xxyp.rdlock.config.RdLockProperties;
import com.bsd.xxyp.rdlock.executor.LockExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author wangcheng
 * @Date: 2022/12/12
 * @Description:
 */
@Slf4j
public class LockHolder implements EnvironmentAware,InitializingBean {
    private String lockPrefix;
    private Map<Class<? extends LockExecutor> ,LockExecutor> lockExecutorMap = new HashMap<>();
    private List<LockExecutor> lockExecutors;
    private LockExpressionEvaluator parseEvaluator;
    private LockEvaluationContextProvider evaluationContextProvider;
    private LockAnnotationFieldParse lockAnnotationFieldParse;
    private RdLockProperties rdLockProperties;
    private LockExecutor currentLockExecutor;

    public LockHolder(List<LockExecutor> lockExecutors, RdLockProperties rdLockProperties) {
        this.lockExecutors = lockExecutors;
        this.rdLockProperties = rdLockProperties;
    }

    public LockExecutor obtainLockExecutor(LockExecutor.Executor executor) {
        if (executor !=null) {
            LockExecutor customExecutor = lockExecutorMap.get(executor.getLockExecutor());
            if (customExecutor != null) {
                return customExecutor;
            }
        }
        return currentLockExecutor;
    }

    public LockInfo buildLockInfo(String lockKey, Long waitTime, Long lockTimeOut) {
        Assert.hasText(lockKey,"rdLock exception: lock key is blank");
        return LockInfo.builder()
                .lockKey(lockKey)
                .waitTime(waitTime)
                .lockTimeout(lockTimeOut)
                .build();
    }


    public LockExpressionEvaluator getParseEvaluator() {
        return parseEvaluator;
    }

    public LockEvaluationContextProvider getEvaluationContextProvider() {
        return evaluationContextProvider;
    }

    public LockAnnotationFieldParse getLockAnnotationFieldParse() {
        return lockAnnotationFieldParse;
    }

   public String getLockPrefix() {
        return lockPrefix;
   }


    @Override
    public void afterPropertiesSet()  {
        for (LockExecutor executor : lockExecutors) {
            lockExecutorMap.put(executor.getClass(),executor);
            log.info("rdLock lockExecutor load finish: {}",executor.getClass().getSimpleName());
        }
        this.parseEvaluator = new LockExpressionEvaluator();
        this.evaluationContextProvider = new LockEvaluationContextProvider();
        this.lockAnnotationFieldParse = new LockAnnotationFieldParse();
        String executorName = rdLockProperties.getExecutorName();
        LockExecutor executor = null;
        if (StringUtils.isBlank(executorName)) {
            if (!CollectionUtils.isEmpty(lockExecutors)) {
                executor = lockExecutors.get(0);
            }
        }else {
            executor = lockExecutorMap.get(LockExecutor.Executor.getExecutor(executorName));
        }
        Assert.notNull(executor,"rdLock error: load lockExecutor failure must has one");
        this.currentLockExecutor = executor;
    }

    @Override
    public void setEnvironment(Environment environment) {
        String prefix = rdLockProperties.getPrefix();
        if (StringUtils.isBlank(prefix)) {
            String property = environment.getProperty("spring.application.name");
            if (StringUtils.isNotBlank(property)) {
                this.lockPrefix = property.replace("-",":");
            }
        }else {
            this.lockPrefix = prefix;
        }

    }
}
